package gate

import (
	"bufio"
	crand "crypto/rand"
	"encoding/binary"
	"fmt"
	"gitee.com/goxiang2/server/lib/lib_rpc"
	"gitee.com/goxiang2/server/lib/lib_util"
	"io"
	"log"
	"math/big"
	"net"
	"time"
)

const (
	CONNECT_BUFF_SZIE = 4096
)

type gxConn struct {
	Id     int64 //连接id
	Conn   net.Conn
	Reader *bufio.Reader
	Writer *bufio.Writer
	Remote string

	Uid    uint32 //用户id
	GameId string //用户分配逻辑服务器id
	Key    []byte //通讯密钥

	Close chan interface{} //连接断开
	Out   chan []byte

	ReqBeginTime     int64 //请求到达时刻（毫秒）
	BeginTime        int64 //连接起始时间
	EndTime          int64 //连接结束时间
	Count            int32 //连接请求次数
	TotalPackageSize int64 //连接请求包累计大小
	MinDelayOperate  int32 //服务端处理最小延迟操作
	MinDelay         int64 //服务端处理最小延迟
	MaxDelayOperate  int32 //服务端处理最大延迟操作
	MaxDelay         int64 //服务端处理最大延迟
}

//randomBigInt 生成一个指定长度的随机数
func randomBigInt(nb int) *big.Int {
	b := make([]byte, nb)
	_, err := crand.Read(b)
	if err != nil {
		return nil
	}
	r := new(big.Int)
	r.SetBytes(b)
	return r
}

//connHandshake 连接初始通讯密钥，DH密钥交换算法，生成一个256位的密钥
//假如用户A和用户B希望交换一个密钥。
//取素数p和整数a，a是p的一个原根，公开a和p。
//A选择随机数XA<p，并计算YA=a^XA mod p。
//B选择随机数XB<p，并计算YB=a^XB mod p。
//每一方都将X保密而将Y公开让另一方得到。
//A计算密钥的方式是：K=(YB) ^XA modp
//B计算密钥的方式是：K=(YA) ^XB modp
func connHandshake(conn net.Conn) ([]byte, error) {
	//握手阶段，控制消息超时时间，握手结束后，解除控制，连接存活由心跳处理
	if err := conn.SetReadDeadline(time.Now().Add(time.Millisecond * 500)); err != nil {
		return nil, err
	}
	defer func() {
		if err := conn.SetReadDeadline(time.Time{}); err != nil {
			conn.Close()
		}
	}()

	//发送一个后面用来验证key的32字节随机字符串
	random := lib_util.GetRandomString(32)
	_, err := conn.Write([]byte(random))
	if err != nil {
		return nil, err
	}

	//接收三个256位的大数,p,g,cx
	var buff [129]byte
	_, err = io.ReadFull(conn, buff[:])
	if err != nil {
		return nil, err
	}

	p := new(big.Int)
	g := new(big.Int)
	cx := new(big.Int)
	sx := new(big.Int)
	p.SetBytes(buff[:64])     //64byte
	g.SetBytes(buff[64:65])   //1byte
	cx.SetBytes(buff[65:129]) //64byte

	s := randomBigInt(256)
	sx.Exp(g, s, p)

	//生成通讯密钥
	x := new(big.Int)
	x.Exp(cx, s, p)
	key := x.Bytes()

	//把自己的dh公钥发给客户端
	_, err = conn.Write(sx.Bytes())
	if err != nil {
		return nil, err
	}

	//接收客户端加密32字节随机字符串后的数据
	var enBuff [48]byte
	_, err = io.ReadFull(conn, enBuff[:])
	if err != nil {
		return nil, err
	}

	//解密并验证32字节随机字符串
	deBuff, err1 := lib_util.AesDecrypt(enBuff[:], key[:32])
	if err1 != nil {
		return nil, err1
	}

	if string(deBuff) != random {
		return nil, fmt.Errorf("handshake key error")
	}

	//aes-cbc-256 只需要32个字节的密钥
	return key[:32], nil
}

// connRun 启动一个新连接
func connRun(conn net.Conn) {
	defer lib_util.Recover()

	//握手 生成一个32字节的密钥
	key, err := connHandshake(conn)
	if err != nil {
		log.Printf("handshake fail, remote: %v, %v\n", conn.RemoteAddr().String(), err)
		conn.Close()
		return
	}

	info := &gxConn{
		Conn:   conn,
		Key:    key,
		Remote: conn.RemoteAddr().String(),
		Reader: bufio.NewReaderSize(conn, CONNECT_BUFF_SZIE),
		Writer: bufio.NewWriterSize(conn, CONNECT_BUFF_SZIE),

		Out: make(chan []byte, 32),

		Close: make(chan interface{}),
	}

	go info.processSend()
	info.processRecv()
}

func (conn *gxConn) Return(cmd uint16, seq uint16, ret uint16, rspBuf []byte) {
	l := 0
	if rspBuf != nil {
		l = len(rspBuf)
	}
	buf := make([]byte, 6+l)

	binary.BigEndian.PutUint16(buf[:2], cmd)
	binary.BigEndian.PutUint16(buf[2:4], seq)
	binary.BigEndian.PutUint16(buf[4:6], ret)
	copy(buf[6:], rspBuf)
	conn.send(buf)
}

func (conn *gxConn) send(buf []byte) {
	select {
	case conn.Out <- buf:
	case <-conn.Close:
	}
}

func (conn *gxConn) read() ([]byte, error) {
	buff := make([]byte, 2)
	_, err := io.ReadFull(conn.Reader, buff)
	if err != nil {
		return nil, err
	}
	len := binary.BigEndian.Uint16(buff)

	buff = make([]byte, len)

	_, err = io.ReadFull(conn.Reader, buff)
	if err != nil {
		log.Printf("read connect2 %v error, %v\n", conn.Remote, err)
		return nil, err
	}

	deBuff, err1 := lib_util.AesDecrypt(buff, conn.Key)
	if err1 != nil {
		log.Printf("%v aes decrypt error, %v\n", conn.Remote, err)
		return nil, err1
	}

	return deBuff, nil
}

// processSend 处理连接需要发送的消息
func (conn *gxConn) processSend() {
	defer lib_util.Recover()

	var buf [2]byte
	for {
		select {
		case msg := <-conn.Out:
			enBuff, err := lib_util.AesEncrypt(msg, conn.Key)
			if err != nil {
				log.Printf("%v aes encrypt error, %v\n", conn.Remote, err)
				conn.Conn.Close()
				return
			}
			binary.BigEndian.PutUint16(buf[:], uint16(len(enBuff)))

			conn.Writer.Write(buf[:])
			conn.Writer.Write(enBuff)
			conn.Writer.Flush()
		case <-conn.Close:
			return
		case <-time.After(20 * time.Second):
			log.Printf("connect %v timeout\n", conn.Remote)
			conn.Conn.Close()
			return
		}
	}
}

// processRecv 处理连接读取消息
func (conn *gxConn) processRecv() {
	defer func() {
		conn.Conn.Close()
		close(conn.Close)
	}()

	for {
		buff, err := conn.read()
		if err != nil {
			// log.Println(err)
			return
		}

		cmd := binary.BigEndian.Uint16(buff[:2])
		seq := binary.BigEndian.Uint16(buff[2:4])
		if cmd == 0 {
			//心跳
			conn.Return(cmd, seq, 0, nil)
		} else if serviceMethod := lib_rpc.FindServiceMethod(uint32(cmd)); serviceMethod != "" {
			//forward to login
			ret, rspBuff, retErr := lib_rpc.RpcRawCall(serviceMethod, buff[4:])
			if retErr != nil {
				ret = 1
			}
			if ret != 0 {
				conn.Return(cmd, seq, uint16(ret), nil)
			}
			conn.Return(cmd, seq, uint16(ret), rspBuff)
		} else {
			log.Printf("connect %v cmd error, %v\n", conn.Remote, cmd)
			return
		}
	}
}
